﻿using System;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Bling.DSL;
using Bling.WPF;
using Bling.WPF.Custom;
using Bling.WPF.Util;
using Bling.Core;
using Bling.Util;
using Bling.Physics.UI;
using Bling.Physics;
using Bling.Properties;
using Bling.Mixins;
using Bling.Blocks;
using Bling.Shapes;
using Bling.Graphics;
using Bling.Matrices;
using Bling.Angles;
using Bling.WPF3D;
namespace Bling.Tutorial {
  public class VirtualSphereTest : Canvas {
    public VirtualSphereTest() {
      var Canvas = this.Bl();
      Canvas.Background = Brushes.White;
      // completely composed transform for use in 2D simulation.
      Matrix4Bl MainTransform;
      Matrix4Bl WorldTransform;
      // Use two rotations to more accurately represent transient rotation, rotation A is the current rotation
      // while rotation B is the transient rotation. These are anonymous dependency properties. 
      QuaternionBl RotateA = this.Property(QuaternionBl.Identity);
      QuaternionBl RotateB = this.Property(QuaternionBl.Identity);
      // we are row-based, so application occurs from right to left, so rotate b has to occur on the left of the multiply
      // sign, just as in matrix compositions. 
      QuaternionBl UseRotate = RotateB * RotateA;
      // Our camera.
      PerspectiveCameraBl Camera;
      // topology for the cube we want to render.
      var Cube = new CubeMesh();
      {
        var SliderZ = new LabelSliderBl(Canvas) { Bottom = Canvas.Height, Left = 0, Minimum = 0, Maximum = 1, LabelName = "Z", Value = .3, ZIndex = 4 };
        var SliderY = new LabelSliderBl(Canvas) { Bottom = SliderZ.Top, Left = 0, Minimum = -10, Maximum = +10, LabelName = "Y", Value = 0, ZIndex = 4 };
        var SliderX = new LabelSliderBl(Canvas) { Bottom = SliderY.Top, Left = 0, Minimum = -10, Maximum = +10, LabelName = "X", Value = 0, ZIndex = 4 };
        var SliderFOfView =
          new LabelSliderBl(Canvas) { 
            Bottom = SliderX.Top, Left = 0, Minimum = 0, Maximum = 2d, LabelName = "FView", Value = .1, ZIndex = 4 };

        Double3Bl WorldCenter = new Double3Bl(0, 0, 0);
        Camera = new PerspectiveCameraBl() {
          Position = WorldCenter + new Double3Bl(0, 0, -8),
          Direction = { Look = WorldCenter },
          FieldOfView = SliderFOfView.Value.PI().Degrees,
        };
        WorldTransform = this.Property(Matrix4Bl.MakeTranslate(new Double3Bl(SliderX, SliderY, 0d)) *
          Matrix4Bl.MakeScale(SliderZ.Value.XXX()) * (UseRotate).Matrix);
        MainTransform = this.Property(Camera.Matrix * WorldTransform);
      }


      //var Points = new Double2Bl[2, 2, 2];
      var ellipses = new EllipseBl[Cube.VertexCount.CurrentValue];
      DoubleBl minZ = double.PositiveInfinity;
      DoubleBl maxZ = double.NegativeInfinity;

      var MinMax = new Double2Bl(1, 3);
      Func<int, Double3Bl> PointFor = (i) => {
        var xyz = Cube[i];
        var q00 = MainTransform.Transform(xyz.Position);
        var q0 = (q00 + 1d) / 2d;
        var q = new Double2Bl(q0.X, 1d - q0.Y);
        q = q * Canvas.Size;
        return new Double3Bl(q, q0.Z);
      };

      for (int i = 0; i < Cube.VertexCount.CurrentValue; i++) {
        var q = PointFor(i);
        var Z = 1d - q.Z;
        minZ = minZ.Min(Z);
        maxZ = maxZ.Max(Z);
        ellipses[i] = new EllipseBl(Canvas) {
          Fill = i.SelectColor(),
          CenterPosition = q.XY(),
          Size = 10d + (q.Z - MinMax.X) / (MinMax.Y - MinMax.X) * 10d,
          ZIndex = 4,
          ToolTip = "".Bl() + q.ToStringBl(100d),
        };
      }
      Func<int, Double2Bl> PointFor0 = j => ellipses[j].CenterPosition;
      for (int i = 0; i < Cube.PrimitiveCount.CurrentValue; i++) {
        var tri = Cube.Indices(i);
        var p0 = tri[0].CurrentValue;
        var p1 = tri[1].CurrentValue;
        var p2 = tri[2].CurrentValue;
        /*
        var p0 = Cube.Indices[i + 0];
        var p1 = Cube.Indices[i + 1];
        var p2 = Cube.Indices[i + 2];
        */
        //new Triangle
        new TriangleBl(Canvas) {
          A = PointFor0(p0), B = PointFor0(p1), C = PointFor0(p2),
          Fill = i.SelectColor(),
          ZIndex = 1, IsHitTestVisible = false,
          Opacity = .1,
        };
      }
      // virtual sphere sphere
      var sz = Canvas.Size - 20d;
      {
        var SphereDiameter = sz.X.Min(sz.Y);
        var sphere = new EllipseBl(Canvas) {
          Size = SphereDiameter,
          CenterPosition = Canvas.CenterSize,
          ZIndex = 2,
          Fill = Brushes.LightGray,
          Opacity = .1,
        };
        var outer = new EllipseBl(Canvas) {
          Size = SphereDiameter + 10d,
          CenterPosition = Canvas.CenterSize,
          ZIndex = 1,
          Fill = Brushes.Black,
          Opacity = .1,
        };
        Func<DoubleBl, Double2Bl> FX = (d) =>
          Canvas.CenterSize + (2d.PI() * ((d + 1d) / 2d)).SinCos * ((SphereDiameter / 2d) + 2.5);
        new EllipseBl(Canvas) {
          Fill = Brushes.Red,
          Stroke = { Thickness = 0 },
          Size = 10d,
          ZIndex = 2,
          IsHitTestVisible = false,
          CenterPosition = FX((UseRotate).X)
        };
        new EllipseBl(Canvas) {
          Fill = Brushes.Blue,
          Stroke = { Thickness = 0 },
          Size = 10d,
          ZIndex = 2,
          IsHitTestVisible = false,
          CenterPosition = FX((UseRotate).Y)
        };
        new EllipseBl(Canvas) {
          Fill = Brushes.White,
          Stroke = { Thickness = 0 },
          Size = 10d,
          ZIndex = 2,
          IsHitTestVisible = false,
          CenterPosition = FX((UseRotate).Z)
        };
        new EllipseBl(Canvas) {
          Fill = Brushes.Pink,
          Stroke = { Thickness = 0 },
          Size = 10d,
          ZIndex = 2,
          IsHitTestVisible = false,
          CenterPosition = FX((UseRotate).W)
        };
        var MouseAt = sphere.Mouse.Position / sphere.Size;
        MouseAt = MouseAt * 2d - 1d;
        MouseAt = MouseAt * new Double2Bl(-1d, +1d); // flip X
        var MouseAt3D = new Double3Bl(MouseAt, (1d - MouseAt.LengthSquared).Max(0d).Sqrt);
        {
          var MouseInit = this.Property(Double3Bl.Unit);

          Action SetMouseInit = Double3Bl.Assign(MouseInit, MouseAt3D.RawNormalize);
          var q = MouseAt3D.RawNormalize.AngleBetween(MouseInit);
          var qA = q;
          var ctl = BoolBl.State(() => {
            return Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
          });

          IntBl Idx = 0;
          Idx = (qA[1].Abs > qA[Idx].Abs).Condition(1, Idx);
          //Idx = (qA[2].Abs > qA[Idx].Abs).Condition(2, Idx);
          var mask = new Double3Bl(
            (Idx == 0).Condition(1d, 0d),
            (Idx == 1).Condition(1d, 0d),
            (Idx == 2).Condition(0d, 0d));
          var qXYZ = qA.XYZ() * mask;
          // renormalize.
          qA = new QuaternionBl(qXYZ, qA.W).RawNormalize;
          qA = q; // ctl.Condition(qA, q);

          Action SetRotateB = QuaternionBl.Assign(RotateB, qA);
          Action SetRotateA = QuaternionBl.Assign(RotateA, UseRotate);
          Action ResetRotateB = QuaternionBl.Assign(RotateB, QuaternionBl.Identity);

          sphere.IsHitTestVisible = false;
          outer.InstallDragHandler(() => {
            SetMouseInit();
            return true;
          }, SetRotateB, () => {
            SetRotateA();
            ResetRotateB();
          });
          /*
          new LabelBl(Canvas) {
            RightTop = new Double2Bl(Canvas.Width, 0d),
            Content =
            RotateA.ToStringBl(100d) +
            " " + RotateB.ToStringBl(100d) +
            " " + (RotateA * RotateB).ToStringBl(100d) +
            " " + (RotateA * RotateB).Angle.PI.ToStringBl(100d) +
            " " + (RotateA * RotateB).Axis.ToStringBl(100d) +
              "",
          };*/
        }
      }
    }
  }
}